package cn.lcsjsxy.yxc.lambdalean;

import cn.lcsjsxy.yxc.renerics.Employee;
import cn.lcsjsxy.yxc.renerics.Person;
import org.junit.Test;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/*
 * 方法引用
 * 1.理解：
 * 方法引用可以看作是Lambda表达式深层次的表达,本质上就是Lambda表达式，发放引用也是函数式接口的实例
 *
 * 2.使用情景：
 * 当要传递给Lambda体的操作，已经有实现的方法了，可以使用方法引用
 *
 * 3.格式：
 * 类（或对象）：：方法名
 *
 * 4.分为如下的三种情况：
 *      》对象::非静态方法
 *      》类::静态方法
 *      》类::静态方法
 *
 * 5.要求：
 * 》要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同！（针对于情况1和情况2）
 * 》当函数式接口方法的第一个参数是需要引用方法的调用者，并且第二个参数是需要引用方法的参数（或无参数）时：ClassName::methodName
 *
 * 6.使用建议
 * 如果给函数式接口提供实例，恰好满足方法引用的使用情景，大家就可以考虑使用方法引用给函数式接口提供实例。如果大家不熟悉方法引用，那么还可以
 * 使用Lambda表达式。
 *
 */
public class MethodReference {
    //情况一：对象：：实例方法
    //Consumer中的void accept(T t)
    //PrintStream中的void println(T t)
    @Test
    public void test1() {
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("北京");

        System.out.println("***************************************");
        PrintStream ps = System.out;
        Consumer<String> con2 = ps::println;
        con2.accept("beijing");

        System.out.println("****************************************");

        Person p = new Person();
        Supplier sup = () -> p.getClass();
        System.out.println(sup.get());

        System.out.println("*******************************************");

        Supplier sup2 = p::getClass;
        System.out.println(sup2.get());
    }

    //情况二：类：：静态方法
    //Comparator中的int compare(T t1,T t2)
    //Integer中的int compare(T t1,T t2)
    @Test
    public void test2() {
        Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
        System.out.println(com1.compare(12, 21));

        System.out.println("************************");

        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(2, 1));

        System.out.println("*****************************");

        Function<Double, Long> func1 = d -> Math.round(d);
        System.out.println(func1.apply(12.3));

        System.out.println("*****************************");

        Function<Double, Long> func2 = Math::round;
        System.out.println(func2.apply(12.6));
    }

    //情况三：类：：实例方法（有难度）
    //Comparator中的int compare(T t1,T t2)
    //String中的int t1.compareTo(t2)
    @Test
    public void test3(){
        Comparator<String> com1=(s1,s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("avc","avb"));

        System.out.println("*****************************");

        Comparator<String> com2=String::compareTo;
        System.out.println(com2.compare("abb","acc"));

        System.out.println("******************************");

        BiPredicate<String,String> pre1=(s1,s2) -> s1.equals(s2);
        System.out.println(pre1.test("abc","abc"));

        System.out.println("*******************************");

        BiPredicate<String,String> pre2=String::equals;
        System.out.println(pre1.test("abc","abd"));
    }

    /* 一、构造器引用
    *       和方法引用类似，函数式接口的抽象方法的形参列表和构造器的形参列表一致
    *       抽象方法的返回值类型即为构造器所属的类的类型
    *
    * 二、数组引用
    *       大家可以把数组看成一个特殊的类，则写法与构造器引用一致
    * */

    //方法引用
    //Supplier中的T get()
    @Test
    public void test4(){
        Supplier<Person> sup = new Supplier<Person>() {
            @Override
            public Person get() {
                return new Person();
            }
        };
        System.out.println(sup.get());

        System.out.println("**********************************");

        Supplier<Person> sup1=() -> new Person();
        System.out.println(sup1.get());

        System.out.println();

        Supplier<Person> sup2=Person::new;
        System.out.println(sup2.get());
    }

    //数组引用
    //Function中的R apply(T t)
    @Test
    public void test5(){
        Function<Integer,String[]> func1 = length -> new String[length];
        String[] arr1=func1.apply(5);
        System.out.println(Arrays.toString(arr1));

        System.out.println("****************************");

        Function<Integer,String[]> func2 = String[]::new;
        String[] arr2=func1.apply(6);
        System.out.println(Arrays.toString(arr2));
    }
}
